package com.hivekion.files.controller;

import com.hivekion.common.Date.DateUtils;
import com.hivekion.common.exception.BusinessException;
import com.hivekion.files.domain.FileInfo;
import com.hivekion.files.domain.vo.MergeFileResult;
import com.hivekion.files.domain.vo.VerifyUploadResult;
import com.hivekion.files.mapper.FileInfoMapper;
import com.hivekion.files.service.IFileService;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/file")
public class FileController {

      @Autowired
      IFileService fileService;
      @Autowired
      FileInfoMapper fileInfoMapper; // 文件库，不需要可以去掉

      @Value("${uploadPath.projectfile}")
      private String uploadPath;

      @PostMapping("/verifyUpload")
      public VerifyUploadResult verifyUpload(String filename, String fileHash) {
	    VerifyUploadResult result = new VerifyUploadResult();
	    List<String> files = new ArrayList<>();
	    FileInfo fileInfo = fileService.findFileByHash(fileHash);
	    if (fileInfo != null) {
		  result.setShouldUpload(true);
		  result.setFilePath(fileInfo.getFilePath());
	    } else {
		  String filePath = uploadPath + "/" + filename;
		  File _file = new File(filePath);
		  if (_file.exists()) {
			//                try {
			//                    String hash = md5HashCode(filePath);
			//                    if (hash.equals(fileHash)) {
			//                        result.setShouldUpload(true);
			//                        result.setFilePath(filename);
			//                    }
			//                } catch (FileNotFoundException e) {
			//                    e.printStackTrace();
			//                }
			result.setShouldUpload(true);
			result.setFilePath(filename);
		  } else {
			String file_name =
				Arrays.stream(StringUtils.split(filename, ".")).findFirst().get();
			String fileDir = uploadPath + "/" + file_name;
			File dir = new File(fileDir);
			if (!dir.exists()) { // 目录不存在
			      result.setShouldUpload(false);
			      result.setUploadedList(files);
			} else {
			      File[] array = dir.listFiles(); // 获取目录下所有文件
			      for (int i = 0; i < array.length; i++) {
				    if (array[i].isFile()) {
					  files.add(array[i].getName());
				    }
			      }
			      result.setUploadedList(files);
			}
		  }
	    }
	    return result;
      }

      @PostMapping("/UploadFile")
      public String UploadFile(MultipartFile chunk, String hash, String filename, String fileHash) {
	    String fileName = chunk.getOriginalFilename(); // 获取文件名
	    String file_name = Arrays.stream(StringUtils.split(filename, ".")).findFirst().get();
	    String fileDir = uploadPath + "/" + file_name;
	    File dir = new File(fileDir);
	    if (!dir.exists()) { // 目录不存在
		  dir.mkdirs();
	    }
	    try {
		  String newFileName = fileDir + "/" + StringUtils.split(hash, "-")[1];
		  File newFile = new File(newFileName);
		  chunk.transferTo(newFile);
	    } catch (IOException e) {
		  e.printStackTrace();
	    }
	    return fileName;
      }

      @PostMapping("/mergeFile")
      @ApiParam(value = "合并切片")
      public MergeFileResult mergeFile(String fileHash, String fileName) {
	    MergeFileResult result = new MergeFileResult();
	    String file_name = Arrays.stream(StringUtils.split(fileName, ".")).findFirst().get();
	    String fileDir = uploadPath + "/" + file_name;
	    File dir = new File(fileDir);
	    if (!dir.exists()) { // 目录不存在
		  throw new BusinessException(500, "未找到切片保存路径");
	    }
	    String newFileName = uploadPath + "/" + fileName; // 创建合并后的文件
	    File file = new File(newFileName);
	    if (file.exists()) {
		  file.delete();
	    }
	    try {
		  BufferedOutputStream destOutputStream =
			  new BufferedOutputStream(new FileOutputStream(newFileName));
		  File[] array = dir.listFiles(); // 获取目录下所有文件
		  for (int i = 0; i < array.length; i++) {
			File partFile = new File(fileDir, i + "");
			if (partFile.isFile()) {
			      // 循环将每个分片的数据写入目标文件
			      byte[] fileBuffer = new byte[1024]; // 文件读写缓存
			      int readBytesLength = 0; // 每次读取字节数
			      BufferedInputStream sourceInputStream =
				      new BufferedInputStream(new FileInputStream(partFile));
			      while ((readBytesLength = sourceInputStream.read(fileBuffer)) != -1) {
				    destOutputStream.write(fileBuffer, 0, readBytesLength);
			      }
			      sourceInputStream.close();
			}
		  }
		  destOutputStream.flush();
		  destOutputStream.close();
	    } catch (FileNotFoundException e) {
		  e.printStackTrace();
	    } catch (IOException e) {
		  e.printStackTrace();
	    } finally {
		  try {
			String hash = md5HashCode(newFileName);
			if (!hash.equals(fileHash)) {
			      throw new BusinessException(500, "文件上传异常，hash值不一致");
			}
			System.out.println(hash);

		  } catch (FileNotFoundException e) {
			e.printStackTrace();
		  }
		  result.setFileName(fileName);
		  result.setFilePath(fileName);
		  result.setFileHash(fileHash);
		  return result;
	    }
      }

      private String md5HashCode(String filePath) throws FileNotFoundException {
	    FileInputStream fis = new FileInputStream(filePath);
	    return md5HashCode(fis);
      }

      private String md5HashCode(InputStream fis) {
	    try {
		  // 拿到一个MD5转换器,如果想使用SHA-1或SHA-256，则传入SHA-1,SHA-256
		  MessageDigest md = MessageDigest.getInstance("MD5");

		  // 分多次将一个文件读入，对于大型文件而言，比较推荐这种方式，占用内存比较少。
		  byte[] buffer = new byte[1024];
		  int length = -1;
		  while ((length = fis.read(buffer, 0, 1024)) != -1) {
			md.update(buffer, 0, length);
		  }
		  fis.close();
		  // 转换并返回包含16个元素字节数组,返回数值范围为-128到127
		  byte[] md5Bytes = md.digest();
		  BigInteger bigInt = new BigInteger(1, md5Bytes); // 1代表绝对值
		  return bigInt.toString(16); // 转换为16进制
	    } catch (Exception e) {
		  e.printStackTrace();
		  return "";
	    }
      }

      @PostMapping("/uploadSignPhoto")
      @ApiParam(value = "上传签名图片")
      public String uploadSignPhoto(@RequestParam("file") MultipartFile file) {
	    if (file.getSize() == 0 || file.isEmpty()) {
		  throw new BusinessException(500, "签名不能为空");
	    }
	    String newFileName = "";
	    String originalFilename = file.getOriginalFilename();
	    String randomFileName = DateUtils.getRandomFileName();
	    String fileName = randomFileName + "_" + originalFilename;
	    String nowDay = DateUtils.getNowDay();
	    String fileDir = File.separator + nowDay;
	    File dir = new File(uploadPath + fileDir);
	    if (!dir.exists()) { // 目录不存在
		  dir.mkdirs();
	    }
	    try {
		  newFileName = fileDir + File.separator + fileName;
		  File newFile = new File(uploadPath + File.separator + newFileName);
		  file.transferTo(newFile);
	    } catch (IOException e) {
		  e.printStackTrace();
	    }
	    return newFileName;
      }

}
